#include "Camera.hpp"
#include "GameWorld.h"
#include "Vector2D.h"

float	Camera::mDelay			= 150; // ms
int		Camera::mDistance		= 70;
int		Camera::mSpeed			= 45;
float	Camera::mDeaccelRate	= 1.0;
int		Camera::mStopScrollingSpeed = 2.5;

Camera::Camera()
{
	m_pClippingPlane = ClipPlane;

	// get location & screen size
	mLocation.x = m_pClippingPlane->GetX();
	mLocation.y = m_pClippingPlane->GetY();
	mLocation.w = DataLoader->WINDOW_WIDTH;
	mLocation.h = DataLoader->WINDOW_HEIGHT;

	// give default values to everything else
	mMouse.x = 0;
	mMouse.y = 0;

	mAccel = false;
	mDeaccel = false;
	mResetAccel = false;
	mRunBigUpdate = true;
	mZoomPercent = 1.0f;
	mDeaccelValue = 1;
	mPrevDir.x = DataLoader->WINDOW_WIDTH / 2;
	mPrevDir.y = DataLoader->WINDOW_HEIGHT / 2;

	mCurrSpeed = 1;

	mWaitTimer.Stop();
	mAccelTimer.Stop();
}
// Constructor
Camera::Camera(ClippingPlane* clippingPlane)
{
	Camera();
	// init variables
	m_pClippingPlane = clippingPlane;
	mLocation.x = m_pClippingPlane->GetX();
	mLocation.y = m_pClippingPlane->GetY();

}

void Camera::SetClippingPlane(ClippingPlane* clippingPlane)
{
	m_pClippingPlane = clippingPlane;
	mLocation.x = m_pClippingPlane->GetX();
	mLocation.y = m_pClippingPlane->GetY();
}

bool Camera::InScrollingBounds(Coords mouse)
{
	Vector2D test;
	//		left								right
	if((mouse.x < mDistance) || (mouse.x > DataLoader->WINDOW_WIDTH - mDistance))
		return true;

	//		top									bottom
	if((mouse.y < mDistance) || (mouse.y > DataLoader->WINDOW_HEIGHT - mDistance))
		return true;

	// not in bounds, return false
	return false;
}

void Camera::Scroll(Coords mouse)
{
	// scroll
	if(mouse.x > DataLoader->WINDOW_WIDTH - mDistance)
	{
		m_pClippingPlane->Translate(int(mCurrSpeed),0);
		if(m_pClippingPlane->GetX() + m_pClippingPlane->GetWidth() > (DataLoader->GetMapWidth() * TILE_DIMENSIONS))
			m_pClippingPlane->SetX((DataLoader->GetMapWidth() * TILE_DIMENSIONS) - m_pClippingPlane->GetWidth());
	}

	if(mouse.x < mDistance)
	{
		m_pClippingPlane->Translate(int(-mCurrSpeed),0);
		if(m_pClippingPlane->GetX() < 0)
			m_pClippingPlane->SetX(0);					
	}
		
		
	if(mouse.y > DataLoader->WINDOW_HEIGHT - mDistance)
	{
		m_pClippingPlane->Translate(0, int(mCurrSpeed));
		if(m_pClippingPlane->GetY() + m_pClippingPlane->GetHeight() > (DataLoader->GetMapHeight() * TILE_DIMENSIONS))
			m_pClippingPlane->SetY((DataLoader->GetMapHeight() * TILE_DIMENSIONS) - m_pClippingPlane->GetHeight());
	}

	if(mouse.y < mDistance && m_pClippingPlane->GetY())
	{
		m_pClippingPlane->Translate(0,int(-mCurrSpeed));
		if(m_pClippingPlane->GetY() < 0)
			m_pClippingPlane->SetY(0);
	}
}


void Camera::Update(Vector2D mouse, Uint8* keystates)
{

	if(keystates[SDLK_UP] && keystates[SDLK_RIGHT])
		UpdateNoEarlyDelay(TopRight);

	else if(keystates[SDLK_UP] && keystates[SDLK_LEFT])
		UpdateNoEarlyDelay(TopLeft);

	else if(keystates[SDLK_DOWN] && keystates[SDLK_RIGHT])
		UpdateNoEarlyDelay(BottomRight);

	else if(keystates[SDLK_DOWN] && keystates[SDLK_LEFT])
		UpdateNoEarlyDelay(BottomLeft);

	else if(keystates[SDLK_UP])
		UpdateNoEarlyDelay(Top);

	else if (keystates[SDLK_DOWN])
		UpdateNoEarlyDelay(Bottom);

	else if (keystates[SDLK_RIGHT])
		UpdateNoEarlyDelay(Right);

	else if (keystates[SDLK_LEFT])
		UpdateNoEarlyDelay(Left);
	else
	{
		/*	if in bounds and start timer is not running
				start starttimer and set mode to accelerate

			if timer > requiredtime before scroll and (accel or deaccel)

				if we are not in bounds then turn off accelerate mode and enable deaccelerate mode

				if accelerate mode
					velc * time...

				if velc > max_speed
					velc = max_speed

				if deacc mode
					velc/ num...

				if velc < 1
					velc = 0
					disable deaccel

				scroll




		*/


		if(InScrollingBounds(mouse) && !(mWaitTimer.IsRunning()))
		{
			mWaitTimer.Restart();
			mAccel = true;
			mDeaccel = false;
			mResetAccel = true;
			mDeaccelValue = 1;
			mRunBigUpdate = true;
			//Log->AddMessage("Big update set to true");
		}


		if(mWaitTimer.GetElapsedMilliseconds() > mDelay && (mAccel || mDeaccel) && mRunBigUpdate)
		{
			if(mResetAccel)
			{
				mAccelTimer.Restart();
				mResetAccel = false;
				mDeaccelValue = 1.0f;
			}

			if(!(InScrollingBounds(mouse))) // deaccel
			{
				mAccel = false;
				mDeaccel = true;
			}
			else if(InScrollingBounds(mouse)) // accel
			{
				if(mAccel == false) // if we were not just accelerating then we need to restart the accel timer
					mAccelTimer.Restart();

				mAccel = true;
				mDeaccel = false;
				mDeaccelValue = 1.0f;
				mPrevDir = GetDirection(mouse);
			}

			// calculate speed
			mCurrSpeed = (((mSpeed * mAccelTimer.GetElapsedSeconds()))) + 1;
			if(mCurrSpeed > mSpeed) // too fast, slow down
				mCurrSpeed = mSpeed;


			if(mAccel) // scroll if we are accelerating
			{
				Scroll(mouse);
			}

			if(mDeaccel)
			{
				mDeaccelValue += mDeaccelRate;	
				mCurrSpeed /= mDeaccelValue;
						

				if(mCurrSpeed < mStopScrollingSpeed)
				{
					mDeaccelValue = 1;
					mCurrSpeed = 0;
					mDeaccel = false;
					mAccel = false;
					mResetAccel = true;
					mWaitTimer.Stop();
					mRunBigUpdate = false;
				}

				// do a special scroll if we are accelerating
				Scroll(mPrevDir);
			}

			
		}
	}

}

void Camera::UpdateNoEarlyDelay(int direction)
{
	Coords mouse = GetDirection(direction);

	if(InScrollingBounds(mouse) && !(mWaitTimer.IsRunning()))
	{
		mWaitTimer.Restart();
		mAccel = true;
		mDeaccel = false;
		mResetAccel = true;
		mDeaccelValue = 1;
		mRunBigUpdate = true;
	}

	if((mAccel || mDeaccel) && mRunBigUpdate)
	{
		if(mResetAccel)
		{
			mAccelTimer.Resume();
			mDeaccelValue = 1;
			mResetAccel = false;
		}

		if(!(InScrollingBounds(mouse))) // deaccel
		{
			mAccel = false;
			mDeaccel = true;

		}
		else if(InScrollingBounds(mouse)) // accel
		{
			if(mAccel == false) // if we were not just accelerating then we need to restart the accel timer
				mAccelTimer.Restart();

			mAccel = true;
			mDeaccel = false;
			mDeaccelValue = 1.0f;
			mPrevDir = GetDirection(mouse);
		}

		// calculate speed
		mCurrSpeed = (mSpeed * mAccelTimer.GetElapsedMilliseconds()) + 1;
		if(mCurrSpeed > mSpeed) // too fast, slow down
			mCurrSpeed = mSpeed;

		if(mAccel) // scroll if we are accelerating
			Scroll(mouse);

		if(mDeaccel)
		{
			mDeaccelValue += mDeaccelRate;	
			mCurrSpeed /= mDeaccelValue;
					

			if(mCurrSpeed < mStopScrollingSpeed)
			{
				mDeaccelValue = 1;
				mCurrSpeed = 0;
				mDeaccel = false;
				mAccel = false;
				mResetAccel = true;
				mWaitTimer.Stop();
				mRunBigUpdate = false;
			}

			// do a special scroll if we are accelerating
			Scroll(mPrevDir);
		}		
	}
}

Coords Camera::GetDirection(Coords mouse)
{
	int w = DataLoader->WINDOW_WIDTH, h = DataLoader->WINDOW_HEIGHT;
	Coords rtn = Vector2D(w / 2, h / 2);

	// top
	if(mouse.y < mDistance)
	{
		rtn.x = w/2;
		rtn.y = 0;
	}

	// right
	if(mouse.x > DataLoader->WINDOW_WIDTH - mDistance)
	{
		rtn.x = w;
		rtn.y = h/2;
	}

	// bottom
	if(mouse.y > DataLoader->WINDOW_HEIGHT - mDistance)
	{
		rtn.x = w/2;
		rtn.y = h;
	}

	// left
	if(mouse.x < mDistance)
	{
		rtn.x = 0;
		rtn.y = h/2;
	}

	// top left
	if((mouse.y < mDistance) && (mouse.x < mDistance))
	{
		rtn.x = 0;
		rtn.y = 0;
	}

	// top right
	if((mouse.y < mDistance) && (mouse.x > DataLoader->WINDOW_WIDTH - mDistance))
	{
		rtn.x = w;
		rtn.y = 0;
	}

	// bottom left
	if((mouse.y > DataLoader->WINDOW_HEIGHT - mDistance) && (mouse.x < mDistance))
	{
		rtn.x = 0;
		rtn.y = h;
	}

	// bottom right
	if((mouse.y > DataLoader->WINDOW_HEIGHT - mDistance) && (mouse.x > DataLoader->WINDOW_WIDTH - mDistance))
	{
		rtn.x = w;
		rtn.y = h;
	}

	return rtn;

}

Coords Camera::GetDirection(int direction)
{

	int w = DataLoader->WINDOW_WIDTH, h = DataLoader->WINDOW_HEIGHT;
	Coords rtn = Vector2D(w / 2, h / 2);

	// top
	if(direction == Top)
	{
		rtn.x = w/2;
		rtn.y = 0;
	}

	// right
	if(direction == Right)
	{
		rtn.x = w;
		rtn.y = h/2;
	}

	// bottom
	if(direction == Bottom)
	{
		rtn.x = w/2;
		rtn.y = h;
	}

	// left
	if(direction == Left)
	{
		rtn.x = 0;
		rtn.y = h/2;
	}

	// top left
	if(direction == TopLeft)
	{
		rtn.x = 0;
		rtn.y = 0;
	}

	// top right
	if(direction == TopRight)
	{
		rtn.x = w;
		rtn.y = 0;
	}

	// bottom left
	if(direction == BottomLeft)
	{
		rtn.x = 0;
		rtn.y = h;
	}

	// bottom right
	if(direction == BottomRight)
	{
		rtn.x = w;
		rtn.y = h;
	}

	return rtn;

}